﻿using AutoMapper;
using ProjectManageSystemApi.DbEntity;
using ProjectManageSystemApi.Enums;
using ProjectManageSystemApi.Models;
using ProjectManageSystemApi.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ProjectManageSystemApi.Services.Impl
{
    public class RoleMenuServices : IRoleMenuServices
    {
        readonly IFreeSql _fsql;
        readonly IMapper _mapper;
        public RoleMenuServices(IFreeSql fsql, IMapper mapper)
        {
            _fsql = fsql;
            _mapper = mapper;
        }

        public PageVo<IEnumerable<RoleDto>> GetAll(PageBo<RoleParam> pageBo)
        {
            var datas = _fsql.Select<SysCoreRole>();
            if (!string.IsNullOrWhiteSpace(pageBo.Param.Code))
            {
                datas = datas.Where(d => d.Code == pageBo.Param.Code);
            }
            if (!string.IsNullOrWhiteSpace(pageBo.Param.Name))
            {
                datas = datas.Where(d => d.Name == pageBo.Param.Name);
            }
            var total = datas.Count();
            datas = datas.Page(pageBo.Page, pageBo.Size);

            var data = new PageVo<IEnumerable<RoleDto>>
            {
                Page = pageBo.Page,
                Size = pageBo.Size,
                Datas = _mapper.Map<List<RoleDto>>(datas.ToList()),
                Total = total
            };
            return data;
        }

        public bool BindUserRole(long userId, List<long> roleId)
        {
            // 把旧的全部删除
            _fsql.Delete<SysCoreUserRoleLink>().Where(x => x.UserId == userId).ExecuteAffrows();

            // 增加新数据
            var addData = roleId.Select(x => new SysCoreUserRoleLink
            {
                UserId = userId,
                RoleId = x
            });

            var rowCount = _fsql.Insert(addData).ExecuteAffrows();
            return rowCount == roleId.Count;
        }

        public IEnumerable<SystemMenuListDto> GetAllMenuByRoleId(long roleId)
        {
            return _mapper.Map<List<SystemMenuListDto>>(_fsql.Select<SysCoreMenu>()
                .ToList(x => new SystemMenuList
                {
                    SystemMenu = x,
                    HasShow = _fsql.Select<SysCoreRelation>()
                        .Any(y => y.BelongsId == roleId && y.ResourceId == x.Id
                            && y.BelongsType == SystemCoreRelation_BelongsType.Role.GetString()
                            && y.ResourceType == SystemCoreRelation_ResourceType.Menu.GetString())
                }));
        }

        public bool UpdateMenuByRoleId(RoleMenuRequest roleMenu)
        {
            // 把旧的全部删除
            _fsql.Delete<SysCoreRelation>()
                .Where(x => x.BelongsId == roleMenu.RoleId
                    && x.BelongsType == SystemCoreRelation_BelongsType.Role.GetString()).ExecuteAffrows();

            // 增加新数据
            var addData = roleMenu.MeunIds.Select(x => new SysCoreRelation
            {
                BelongsId = roleMenu.RoleId,
                BelongsType = SystemCoreRelation_BelongsType.Role.GetString(),
                ResourceId = x,
                ResourceType = SystemCoreRelation_ResourceType.Menu.ToString(),
                Status = (int)SystemCoreRelation_Status.Valid
            });
            return _fsql.Insert<SysCoreRelation>(addData).ExecuteAffrows() == roleMenu.MeunIds.Count;
        }

        public List<SystemMenuDto> GetMenuByUserId(long userId)
        {
            var data = _fsql.Select<SysCoreMenu>()
                .Where(x => _fsql.Select<SysCoreRelation>()
                    .Where(y => y.ResourceType == SystemCoreRelation_ResourceType.Menu.ToString()
                        && y.BelongsType == SystemCoreRelation_BelongsType.Role.GetString()
                        && _fsql.Select<SysCoreUserRoleLink>().Any(l => l.UserId == userId && l.RoleId == y.BelongsId))
                    .ToList(r => r.ResourceId).Contains(x.Id))
                .OrderBy(x => x.ParentId)
                .OrderBy(x => x.Order)
                .ToList();
            return _mapper.Map<List<SystemMenuDto>>(data);
        }

        public List<SystemElementDto> GetElementByUserId(long userId)
        {
            var data = _fsql.Select<SysCoreElement>()
                .Where(x => _fsql.Select<SysCoreRelation>()
                    .Where(y => y.ResourceType == SystemCoreRelation_ResourceType.Element.ToString()
                        && y.BelongsType == SystemCoreRelation_BelongsType.Role.GetString()
                        && _fsql.Select<SysCoreUserRoleLink>().Any(l => l.UserId == userId && l.RoleId == y.BelongsId))
                    .ToList(r => r.ResourceId).Contains(x.Id))
                .ToList();
            return _mapper.Map<List<SystemElementDto>>(data);
        }

        /// <summary>
        /// 获取角色权限
        /// </summary>
        /// <returns></returns>
        public RoleAuthResult GetRoleAuth(long roleId)
        {
            var result = new RoleAuthResult();

            // 该角色下所有权限
            var relations = _fsql.Select<SysCoreRelation>().Where(y => y.BelongsType == SystemCoreRelation_BelongsType.Role.GetString() && y.BelongsId == roleId).ToList();
            // 查询所有的菜单
            var data = _fsql.Select<SysCoreMenu>()
                .IncludeMany(x => x.Elements)
                .OrderBy(x=>x.Order)
                .ToList()
                ;

            var menuData = _mapper.Map<List<SysCoreMenu>, List<RoleAuthMenu>>(data); // ParentId 为 0 的排在前面
            result.Menus = SerializeRoleAuth(menuData, relations);

            var authRoleExtra = _fsql.Select<SysCoreRoleExtra>().Where(x => x.RoleId == roleId).ToOne();
            if (authRoleExtra != null)
            {
                result.RoleAuthExtra = _mapper.Map<SysCoreRoleExtra, SysCoreRoleExtraDto>(authRoleExtra);
            }
            return result;
        }

        public List<RoleAuthMenu> SerializeRoleAuth(List<RoleAuthMenu> menuDatas, List<SysCoreRelation> relations)
        {
            var result = new List<RoleAuthMenu>();
            foreach (var menu in menuDatas.OrderBy(x => x.ParentId)) // ParentId 为 0 的排在前面
            {
                // 菜单是否拥有权限
                var relation = relations.FirstOrDefault(x => x.ResourceType == SystemCoreRelation_ResourceType.Menu.GetString()
                    && x.ResourceId == menu.Id);
                menu.HasAuth = relation != null; // 关系表中存在的，表示拥有该权限

                // 元素是否拥有权限
                foreach (var element in menu.Elements)
                {
                    relation = relations.FirstOrDefault(x => x.ResourceType == SystemCoreRelation_ResourceType.Element.GetString()
                        && x.ResourceId == element.Id);
                    element.HasAuth = relation != null;
                }

                // 父菜单
                if (menu.ParentId == 0)
                {
                    result.Add(menu);
                    continue;
                }
                // 子菜单，获取父菜单
                var parentMenu = result.FirstOrDefault(x => x.Id == menu.ParentId);
                if (parentMenu == null)
                {
                    continue;
                }
                parentMenu.Childrens.Add(menu);
            }
            return result;
        }

        public bool DelRoleAuth(long roleId)
        {
            return _fsql.Delete<SysCoreRelation>().Where(x => x.BelongsType == SystemCoreRelation_BelongsType.Role.GetString() && x.BelongsId == roleId).ExecuteAffrows() > 0;
        }
        public bool UpdRoleRelation(UpdRoleAuthResponse roleAuth) 
        {
            List<SysCoreRelation> relations = new List<SysCoreRelation>();
            foreach (var menuId in roleAuth.MenuIdLists)
            {
                if (menuId <= 0) 
                {
                    continue;
                }
                var relation = new SysCoreRelation()
                {
                    BelongsId = roleAuth.RoleId,
                    BelongsType = SystemCoreRelation_BelongsType.Role.GetString(),
                    ResourceId = menuId,
                    ResourceType = SystemCoreRelation_ResourceType.Menu.GetString(),
                    CreateAt = DateTime.Now,
                };
                relations.Add(relation);
            }
            foreach (var elementId in roleAuth.ElementIdLists) 
            { 
                if(elementId <= 0) 
                {
                    continue;
                }
                var relation = new SysCoreRelation() 
                {
                    BelongsId = roleAuth.RoleId,
                    BelongsType = SystemCoreRelation_BelongsType.Role.GetString(),
                    ResourceId = elementId,
                    ResourceType = SystemCoreRelation_ResourceType.Element.GetString(),
                    CreateAt = DateTime.Now,
                };
                relations.Add(relation);
            }
            return relations.Count > 0 && _fsql.Insert(relations).ExecuteAffrows() >= relations.Count;
        }
        public bool UpdRoleExtra(long roleId, SysCoreRoleExtraDto roleData) 
        {
            var data = _mapper.Map<SysCoreRoleExtraDto, SysCoreRoleExtra>(roleData);
            data.RoleId = roleId;
            return _fsql.Update<SysCoreRoleExtra>().SetSource(data).ExecuteAffrows() > 0;
        }
        public bool UpdRoleAuth(UpdRoleAuthResponse roleAuth)
        {
            try
            {
                _fsql.Transaction(() =>
                {
                    DelRoleAuth(roleAuth.RoleId);

                    if (roleAuth.MenuIdLists.Count > 0 && !UpdRoleRelation(roleAuth))
                    {
                        throw new DbTranException("保存 sys_core_relation 表失败");
                    }

                    if (!UpdRoleExtra(roleAuth.RoleId, roleAuth.RoleExtra))
                    {
                        throw new DbTranException("保存 sys_core_role_extra 表失败");
                    }
                });
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}

namespace ProjectManageSystemApi.Models
{
    public class SysCoreMenuDto
    {
        public long Id { get; set; }
        public long ParentId { get; set; }
        public bool IsDefaultSubPage { get; set; } // 是否是默认子叶
        public string Path { get; set; }
        public string Name { get; set; }
        public string Component { get; set; }
        public string Title { get; set; }
        public string Icon { get; set; }
        public List<SysCoreMenuDto> Children { get; set; }
        public List<SysCoreElementDto> Element { get; set; }
    }
    public class SysCoreElementDto
    {
        public long Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
    }

    /// <summary>
    /// 菜单内容
    /// </summary>
    public class RoleAuthMenu
    {
        public long Id { get; set; } // 菜单id
        public long ParentId { get; set; } // 菜单父 id
        public string Name { get; set; } // 菜单名称
        public bool HasAuth { get; set; } // 是否拥有权限
        public List<RoleAuthMenu> Childrens { get; set; } = new List<RoleAuthMenu>();
        public List<RoleAuthElement> Elements { get; set; } = new List<RoleAuthElement>();
    }

    public class RoleAuthElement
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool HasAuth { get; set; } // 是否拥有权限
    }
    //public class SysCoreRoleExtraDto
    //{
    //    public bool IsAdmin { get; set; } = false;
    //    public bool ShowAllProject { get; set; } = false;
    //    public bool LoginPc { get; set; } = false;
    //    public bool LoginPhone { get; set; } = false;
    //}
    public class RoleAuthResult
    {
        public List<RoleAuthMenu> Menus { get; set; } = new List<RoleAuthMenu>();
        public SysCoreRoleExtraDto RoleAuthExtra { get; set; } = new SysCoreRoleExtraDto();
    }
}
